﻿using System;
using System.Drawing;
using DotSpatial.Data;

using System.Collections.Generic;

using DotSpatial.Topology;
using Point = System.Drawing.Point;


namespace FXnaGis
{
    public class FMapArgs : EventArgs
    {


        #region Private Variables

        private readonly Extent _bufferEnvelope;
        private readonly Graphics _graphics;
        private Rectangle _bufferRectangle;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MapArgs"/> class.
        /// </summary>
        /// <param name="bufferRectangle">The buffer rectangle.</param>
        /// <param name="bufferEnvelope">The buffer envelope.</param>
        public FMapArgs(Rectangle bufferRectangle, Extent bufferEnvelope)
        {
            _bufferRectangle = bufferRectangle;
            _bufferEnvelope = bufferEnvelope;
        }

        /// <summary>
        /// Creates a new MapArgs, where the device is also specified, overriding the default buffering behavior.
        /// </summary>
        /// <param name="bufferRectangle"></param>
        /// <param name="bufferEnvelope"></param>
        /// <param name="g"></param>
        public FMapArgs(Rectangle bufferRectangle, Extent bufferEnvelope, Graphics g)
        {
            _bufferRectangle = bufferRectangle;
            _bufferEnvelope = bufferEnvelope;
            _graphics = g;
        }

        #endregion

        #region Properties

        /// <summary>
        /// An optional parameter that specifies a device to use instead of the normal buffers.
        /// </summary>
        public Graphics Device
        {
            get { return _graphics; }
        }

        /// <summary>
        /// Gets the dX
        /// </summary>
        public double Dx
        {
            get { return _bufferRectangle.Width / _bufferEnvelope.Width; }
        }

        /// <summary>
        /// Gets the double valued
        /// </summary>
        public double Dy
        {
            get { return _bufferRectangle.Height / _bufferEnvelope.Height; }
        }

        /// <summary>
        /// Gets the minimum X value
        /// </summary>
        public double MinX
        {
            get { return _bufferEnvelope.MinX; }
        }

        /// <summary>
        /// Gets the maximum Y value
        /// </summary>
        public double MaxY
        {
            get { return _bufferEnvelope.MaxY; }
        }

        /// <summary>
        /// Gets the rectangle dimensions of what the buffer should be in pixels
        /// </summary>
        public Rectangle ImageRectangle
        {
            get { return _bufferRectangle; }
        }

        /// <summary>
        /// Gets the geographic bounds of the content of the buffer.
        /// </summary>
        public Extent GeographicExtents
        {
            get { return _bufferEnvelope; }
        }

        #endregion



        //附加部分

        #region Methods

        /// <summary>
        /// Converts a single point location into an equivalent geographic coordinate
        /// </summary>
        /// <param name="this">This IProj</param>
        /// <param name="position">The client coordinate relative to the map control</param>
        /// <returns>The geographic ICoordinate interface</returns>
        public  Coordinate PixelToProj(Point position)
        {
            double x = Convert.ToDouble(position.X);
            double y = Convert.ToDouble(position.Y);
            if (this.GeographicExtents != null)
            {
                x = x * this.GeographicExtents.Width / this.ImageRectangle.Width + this.GeographicExtents.MinX;
                y = this.GeographicExtents.MaxY - y * this.GeographicExtents.Height / this.ImageRectangle.Height;
            }
            return new Coordinate(x, y, 0.0);
        }

        /// <summary>
        /// Converts a rectangle in pixel coordinates relative to the map control into
        /// a geographic envelope.
        /// </summary>
        /// <param name="this">This IProj</param>
        /// <param name="rect">The rectangle to convert</param>
        /// <returns>An IEnvelope interface</returns>
        public  Extent PixelToProj( Rectangle rect)
        {
            Point tl = new Point(rect.X, rect.Y);
            Point br = new Point(rect.Right, rect.Bottom);
            Coordinate topLeft = PixelToProj(tl);
            Coordinate bottomRight = PixelToProj(br);
            return new Extent(topLeft.X, bottomRight.Y, bottomRight.X, topLeft.Y);
        }

        /// <summary>
        /// Projects all of the rectangles int the specified list of rectangles into geographic regions.
        /// </summary>
        /// <param name="this">This IProj</param>
        /// <param name="clipRects">The clip rectangles</param>
        /// <returns>A List of IEnvelope geographic bounds that correspond to the specified clip rectangles.</returns>
        public List<Extent>  PixelToProj(List<Rectangle> clipRects)
        {
            List<Extent> result = new List<Extent>();
            foreach (Rectangle r in clipRects)
            {
                result.Add(PixelToProj( r));
            }
            return result;
        }

        /// <summary>
        /// Converts a single geographic location into the equivalent point on the
        /// screen relative to the top left corner of the map.
        /// </summary>
        /// <param name="this">This IProj</param>
        /// <param name="location">The geographic position to transform</param>
        /// <returns>A Point with the new location.</returns>
        public  Point ProjToPixel(Coordinate location)
        {

            //对于无效数据，我们怎么办呢//还是报错，让外围区纠正吧

        

            //if (double.IsNaN(location.X)||double.IsNaN(location.Y))
            //{
                

            //    return Point.Empty;
                
            //}




            if (this.GeographicExtents.Width == 0 || this.GeographicExtents.Height == 0) return Point.Empty;
            int x = Convert.ToInt32((location.X - this.GeographicExtents.MinX) *
                                    (this.ImageRectangle.Width / this.GeographicExtents.Width));
            int y = Convert.ToInt32((this.GeographicExtents.MaxY - location.Y) *
                                    (this.ImageRectangle.Height / this.GeographicExtents.Height));
            return new Point(x, y);
        }

        /// <summary>
        /// Converts a single geographic envelope into an equivalent Rectangle
        /// as it would be drawn on the screen.
        /// </summary>
        /// <param name="this">This IProj</param>
        /// <param name="env">The geographic IEnvelope</param>
        /// <returns>A Rectangle</returns>
        public  Rectangle ProjToPixel(Extent env)
        {
            Coordinate tl = new Coordinate(env.MinX, env.MaxY);
            Coordinate br = new Coordinate(env.MaxX, env.MinY);
            Point topLeft = ProjToPixel( tl);
            Point bottomRight = ProjToPixel( br);
            return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
        }

        /// <summary>
        /// Translates all of the geographic regions, forming an equivalent list of rectangles.
        /// </summary>
        /// <param name="this">This IProj</param>
        /// <param name="regions">The list of geographic regions to project</param>
        /// <returns>A list of pixel rectangles that describe the specified region</returns>
        public  List<Rectangle> ProjToPixel( List<Extent> regions)
        {
            List<Rectangle> result = new List<Rectangle>();
            foreach (Extent region in regions)
            {
                if (region == null) continue;
                result.Add(ProjToPixel(region));
            }
            return result;
        }

        /// <summary>
        /// Calculates an integer length distance in pixels that corresponds to the double
        /// length specified in the image.
        /// </summary>
        /// <param name="this">The IProj that this describes</param>
        /// <param name="distance">The double distance to obtain in pixels</param>
        /// <returns>The integer distance in pixels</returns>
        public  double ProjToPixel(double distance)
        {
            return (distance * this.ImageRectangle.Width / this.GeographicExtents.Width);
        }


        #endregion

        #region Properties

        #endregion


    }
}
